package commons.params.ihm;

import algo_lignes_sites.params.DlgParams_ls;
import commons.params.param_component.I_Activator;
import commons.utils.Utils;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.ArrayList;
import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.border.Border;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;


public class MyTable extends javax.swing.JPanel {
	
	private String[] columnNames;
	private Class[] columnTypes;
	private ArrayList<Object[]> data;
	private ArrayList<String>[] possibleValues;

	// couleurs grisée dans la table si le ParamComponent contenant this a un activator décoché
	private I_Activator activator;
	
	// On peut spécifier un message d'erreur par cell, vide si pas d'erreur
	private ArrayList<String[]> errorMsg;
	int nbError;

	// Qques renderer perso
	private TableCellRenderer myDefaultCellRenderer;
	private TableCellRenderer myColorRenderer;

	// ParamComponent qui va traiter les modifications utilisateur de la table
	private ActionListener actionListener;

    public MyTable() {
        initComponents();

		myDefaultCellRenderer = new MyDefaultCellRenderer();
		myColorRenderer = new MyColorRenderer();

		this.labelNbError.setText("");
    }


	@Override
	public void setBorder(Border border) {
		if (jScrollPane1 != null)
			jScrollPane1.setBorder(border);
	}

	@Override
	public void setEnabled(boolean enabled) {
		table.setEnabled(enabled);
		button_InsertRow.setEnabled(enabled);
		button_DeleteRow.setEnabled(enabled);
		button_Down.setEnabled(enabled);
		button_Up.setEnabled(enabled);
		//super.setEnabled(enabled);
	}


	public void initializeTable(String[] columnNames, Class[] columnTypes, ArrayList<Object[]> data) {
		assert (columnNames.length==columnTypes.length);

		this.columnNames = columnNames;
		this.columnTypes = columnTypes;
		this.data = data;

		table.setModel(new MyTableModel());
	}
	
	public void setColumnSize(int numCol, int size) {
		table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		table.getColumnModel().getColumn(numCol).setPreferredWidth(size);
		table.getColumnModel().getColumn(numCol).setWidth(size);
	}

	public void setPossibleValues(ArrayList<String>[] possibleValues) {
		this.possibleValues = possibleValues;
	}

	// Declare un message par case (si null, pas d'erreur)
	//  plus un récapitulatid du nombre d'erreur, en rouge, sous la table
	public void setErrMsg(ArrayList<String[]> errorMsg, int nbError) {
		this.errorMsg = errorMsg;
		this.nbError = nbError;
		if (nbError>0)
			this.labelNbError.setText(""+nbError + " erreurs");
		else
			this.labelNbError.setText("");
	}

	// Uniquement un message texte en rouge sous la table
	public void setSingleErrMsg(String errorMsg) {
		labelNbError.setText(errorMsg);
	}

	public ArrayList<Object[]> getData() {
		return data;
	}

	public void setParamComponentListener (ActionListener paramComponentListener) {
		this.actionListener = paramComponentListener;
	}

	public void setActivator(I_Activator activator) {
		this.activator = activator;
	}
	private boolean isActive() {
		if (activator==null) return true;
		return activator.isActivate();
	}
	
	private void handleModifUSer() {
		// sauvegarde sélection
		int selectedRow = table.getSelectedRow();

		// Appel au listener : c'est un ParamComponent, qui lui-même va prévenir le ParamManager
		actionListener.actionPerformed(null);

		// restauration sélection
		if (selectedRow>=0 && selectedRow<data.size())
			table.getSelectionModel().setSelectionInterval(selectedRow, selectedRow);
	}
	
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        button_InsertRow = new javax.swing.JButton();
        button_DeleteRow = new javax.swing.JButton();
        jScrollPane1 = new javax.swing.JScrollPane();
        table = new MyTable_inner();
        button_Up = new javax.swing.JButton();
        button_Down = new javax.swing.JButton();
        labelNbError = new javax.swing.JLabel();

        button_InsertRow.setIcon(new javax.swing.ImageIcon(getClass().getResource("/res/icons/plus_16.png"))); // NOI18N
        button_InsertRow.setToolTipText("Ajouter une ligne");
        button_InsertRow.setMargin(new java.awt.Insets(0, 4, 0, 4));
        button_InsertRow.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                button_InsertRowActionPerformed(evt);
            }
        });

        button_DeleteRow.setIcon(new javax.swing.ImageIcon(getClass().getResource("/res/icons/croix_16.png"))); // NOI18N
        button_DeleteRow.setToolTipText("Supprime la ligne sélectionnée");
        button_DeleteRow.setMargin(new java.awt.Insets(0, 4, 0, 4));
        button_DeleteRow.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                button_DeleteRowActionPerformed(evt);
            }
        });

        table.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
        table.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null}
            },
            new String [] {
                "Title 1", "Title 2", "Title 3", "Title 4"
            }
        ));
        table.setSelectionBackground(new java.awt.Color(204, 204, 255));
        table.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        table.getTableHeader().setReorderingAllowed(false);
        jScrollPane1.setViewportView(table);

        button_Up.setIcon(new javax.swing.ImageIcon(getClass().getResource("/res/icons/haut_16.png"))); // NOI18N
        button_Up.setToolTipText("Déplace la ligne sélectionnée vers le haut");
        button_Up.setMargin(new java.awt.Insets(0, 4, 0, 4));
        button_Up.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                button_UpActionPerformed(evt);
            }
        });

        button_Down.setIcon(new javax.swing.ImageIcon(getClass().getResource("/res/icons/bas_16.png"))); // NOI18N
        button_Down.setToolTipText("Déplace la ligne sélectionnée vers le bas");
        button_Down.setMargin(new java.awt.Insets(0, 4, 0, 4));
        button_Down.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                button_DownActionPerformed(evt);
            }
        });

        labelNbError.setFont(new java.awt.Font("Trebuchet MS", 0, 10)); // NOI18N
        labelNbError.setForeground(new java.awt.Color(255, 0, 0));
        labelNbError.setText("xx erreurs");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(9, 9, 9)
                .addComponent(button_InsertRow)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(button_DeleteRow)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(button_Up)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(button_Down)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(labelNbError, javax.swing.GroupLayout.DEFAULT_SIZE, 142, Short.MAX_VALUE))
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 295, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 142, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(button_InsertRow)
                    .addComponent(button_DeleteRow)
                    .addComponent(button_Up)
                    .addComponent(button_Down)
                    .addComponent(labelNbError)))
        );
    }// </editor-fold>//GEN-END:initComponents

	private void button_InsertRowActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_button_InsertRowActionPerformed
		((MyTable_inner)table).insertRow();
	}//GEN-LAST:event_button_InsertRowActionPerformed

	private void button_DeleteRowActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_button_DeleteRowActionPerformed
		((MyTable_inner)table).deleteRow();
	}//GEN-LAST:event_button_DeleteRowActionPerformed

	private void button_UpActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_button_UpActionPerformed
		((MyTable_inner)table).moveRowUp();
	}//GEN-LAST:event_button_UpActionPerformed

	private void button_DownActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_button_DownActionPerformed
		((MyTable_inner)table).moveRowDown();
	}//GEN-LAST:event_button_DownActionPerformed


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton button_DeleteRow;
    private javax.swing.JButton button_Down;
    private javax.swing.JButton button_InsertRow;
    private javax.swing.JButton button_Up;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JLabel labelNbError;
    private javax.swing.JTable table;
    // End of variables declaration//GEN-END:variables


	class MyTable_inner extends JTable implements FocusListener {

		public void insertRow() {
			int selectedRow = getSelectedRow();
			// si pas de row selectionnée, on insere en dernière position
			if (selectedRow == -1)
				selectedRow = getRowCount()-1;

			//corrige un petit bug quand clic sur une JComBox editeur et choix de la
			//  même valeur que précédement, la combo reste en mode edition ...
			// il faut donc annuler l'edition, sinon l'insertion d'une row va poser
			//  un souci : c'est la nouvelle row qui va recevoir la valeur du combo
			//  alors qu'elle est censée etre vide au debut ...
			editingCanceled(null);

			//((MyTableModel)getModel()).insertRowAbove(selectedRow);
			((MyTableModel)getModel()).insertRowBelow(selectedRow);
			getSelectionModel().setSelectionInterval(selectedRow+1, selectedRow+1);
			scrollToVisible(selectedRow+1);
		}


		public void deleteRow() {
			int selectedRow = getSelectedRow();
			if (selectedRow == -1)
				return;

			//corrige un petit bug quand clic sur une JComBox editeur et chois de la
			//  même valeur que précédement, la combo reste en mode edition ...
			// il faut donc annuler l'edition, sinon l'insertion d'une row va poser
			//  un souci : c'est la nouvelle row qu iva recevoir la valeur du combo
			//  alors qu'elle est censée etre vide au debut ...
			editingCanceled(null);

			((MyTableModel)getModel()).deleteRow(selectedRow);

			// Reselection d'une ligne
			if (selectedRow > getRowCount()-1)
				// cas de la suppression de la ligne la plus en bas
				// on selectionne un cran au dessus
				selectedRow--;

			if (selectedRow>=0)
				getSelectionModel().setSelectionInterval(selectedRow, selectedRow);
		}


		public void moveRowUp() {
			int selectedRow = getSelectedRow();
			if (selectedRow == -1 || selectedRow == 0)
				return;
			((MyTableModel)getModel()).moveRowUp(selectedRow);
			getSelectionModel().setSelectionInterval(selectedRow-1, selectedRow-1);
			scrollToVisible(selectedRow-1);
		}

		public void moveRowDown() {
			int selectedRow = getSelectedRow();
			if (selectedRow == -1 || selectedRow == getRowCount()-1)
				return;
			((MyTableModel)getModel()).moveRowDown(selectedRow);
			getSelectionModel().setSelectionInterval(selectedRow+1, selectedRow+1);
			scrollToVisible(selectedRow+1);
		}

		private void scrollToVisible(int numRow) {
			if (!(getParent() instanceof JViewport)) {
				return;
			}
			JViewport viewport = (JViewport)getParent();

			// This rectangle is relative to the table where the
			// northwest corner of cell (0,0) is always (0,0).
			int colIndex = 0;
			Rectangle rect = getCellRect(numRow, colIndex, true);

			// The location of the viewport relative to the table
			Point pt = viewport.getViewPosition();

			// Translate the cell location so that it is relative
			// to the view, assuming the northwest corner of the
			// view is (0,0)
			rect.setLocation(rect.x-pt.x, rect.y-pt.y);

			// Scroll the area into view
			viewport.scrollRectToVisible(rect);
		}

		@Override
		public TableCellEditor getCellEditor (int row, int col) {
			//Si colonne de type Color on prend un editeur de couleurs
			if (columnTypes[col] == Color.class)
				return new ColorEditor();

			// Si pas de valeurs specifiees, on prend l'editeur par defaut
			if (possibleValues == null || possibleValues[col] == null || possibleValues[col].isEmpty()) {
				TableCellEditor tce = super.getCellEditor(row, col);

				//SIOUX
				// on demande à l'editeur de prevenir s'il perd le focus, dans ce cas, on fera
				// une validation des paramètre (exemple, en cours d'édition, avant d'appuyer sur Enter
				//  l'utilisateur appuie sur le bouton GO : il faut valider avant !!
				if (tce instanceof DefaultCellEditor) {
					Component comp = ((DefaultCellEditor)tce).getComponent();
					if (!estOnDejaFocusListenerDeCeComponent(comp))
						comp.addFocusListener(this);
				}
				return tce;
			}
			
			else {
				// Si des valeurs specifiees, on met un combo box comme editeur
				JComboBox comboBox = new JComboBox();
				comboBox.setBorder(null);

				// On ajoute en premier la valeur déja presente dans la case
				//  dans le cas ou elle n'est pas présente dans la liste de choix
				// En effet, si un paramètre vaut par défaut une valeur non prevue dans la
				//  liste de choix, l'utilisateur souhaite peut etre pouvoir la conserver
				// Or des le moindre clic, on est obligé de saisir une valeur du combo
				String oldValue = (String) getValueAt(row, col);
				if (oldValue==null) oldValue = "";
//				if (!possibleValues[col].contains(oldValue))
//					comboBox.addItem(getValueAt(row, col));

				for (String value : possibleValues[col])
					comboBox.addItem(value);

				return new DefaultCellEditor(comboBox);
			}
		}

		@Override
		public TableCellRenderer getCellRenderer(int row, int col) {
			// Renderer spécial pour le type Color
			if (columnTypes!=null && columnTypes[col] == Color.class)
				return myColorRenderer;

			// Sinon, renderer par défaut
			return myDefaultCellRenderer;
		}


		@Override
		public void focusGained(FocusEvent e) {
			//System.out.println("J'AI LE FOCUS");
		}

		// Si la table perd son focus, il faut valider la saisie éventuellement en cours.
		// Notamment, si en cours d'édition, on appuie sur le bouton GO, il faut faire une passe de test avant de sortir !!
		@Override
		public void focusLost(FocusEvent e) {
			//System.out.println("J'AI PERDU LE FOCUS !!! ");
			if (isEditing()) {
				getCellEditor().stopCellEditing();
				handleModifUSer();
			}
		}

		// pour eviter de s'ajouter plusieurs fois comme listener ...
		private boolean estOnDejaFocusListenerDeCeComponent(Component comp) {
			for (FocusListener fl : comp.getFocusListeners())
				if (fl==this)
					return true;
			return false;
		}
	}


	class MyTableModel extends AbstractTableModel {

		public MyTableModel() {
		}

		@Override
        public int getColumnCount() {
            return columnNames.length;
        }

		@Override
        public int getRowCount() {
            return data.size();
        }

		@Override
        public String getColumnName(int col) {
            return columnNames[col];
        }

		@Override
        public Object getValueAt(int row, int col) {
            return data.get(row)[col];
        }

		@Override
        public Class getColumnClass(int c) {
            return columnTypes[c];
        }

		@Override
        public boolean isCellEditable(int row, int col) {
			return true;
        }

		@Override
        public void setValueAt(Object value, int row, int col) {
            data.get(row)[col] = value;
            fireTableCellUpdated(row, col);
			handleModifUSer();
        }

		public void insertRowBelow (int row) {
			data.add(row+1,new Object[getColumnCount()]);
			fireTableRowsInserted(row+1, row+1);
			handleModifUSer();
		}
		public void deleteRow (int row) {
			data.remove(row);
			fireTableRowsDeleted(row, row);
			handleModifUSer();
		}

		public void moveRowUp (int row) {
			Object[] temp = data.get(row);
			data.set(row, data.get(row-1));
			data.set(row-1, temp);
			fireTableRowsUpdated(row-1, row);
			handleModifUSer();
		}
		public void moveRowDown (int row) {
			Object[] temp = data.get(row);
			data.set(row, data.get(row+1));
			data.set(row+1, temp);
			fireTableRowsUpdated(row, row+1);
			handleModifUSer();
		}
    }


	public class ColorEditor extends AbstractCellEditor implements TableCellEditor,	ActionListener {
		Color currentColor;
		JButton button;
//		JLabel button;
		JColorChooser colorChooser;
		JDialog dialog;
		protected static final String EDIT = "edit";

		public ColorEditor() {
			button = new JButton();
//			button = new JLabel();
			button.setActionCommand(EDIT);
			button.addActionListener(this);
			//button.setBorderPainted(false);
			button.setBorder(BorderFactory.createMatteBorder(2,3,2,3, table.getSelectionBackground()));

//			button.addMouseListener(new java.awt.event.MouseAdapter() {
//            public void mouseClicked(java.awt.event.MouseEvent evt) {
//                toto(evt);
//            }
//			});

			//Set up the dialog that the button brings up.
			colorChooser = new JColorChooser();
			dialog = JColorChooser.createDialog(button,
											"Choisissez une couleur",
											true,  //modal
											colorChooser,
											this,  //OK button handler
											null); //no CANCEL button handler
		}

//		public void toto(java.awt.event.MouseEvent evt) {
//			System.out.println(evt.getClickCount());
//			if (evt.getClickCount()>=2) {
//				button.setBackground(currentColor);
//				colorChooser.setColor(currentColor);
//				dialog.setVisible(true);
//
//				fireEditingStopped(); //Make the renderer reappear.
//			}
//		}

		@Override
		public void actionPerformed(ActionEvent e) {
			if (EDIT.equals(e.getActionCommand())) {
				//The user has clicked the cell, so
				//bring up the dialog.
				button.setBackground(currentColor);
				colorChooser.setColor(currentColor);
				dialog.setVisible(true);

				fireEditingStopped(); //Make the renderer reappear.

			} else { //User pressed dialog's "OK" button.
				currentColor = colorChooser.getColor();
			}
		}

		//Implement the one CellEditor method that AbstractCellEditor doesn't.
		public Object getCellEditorValue() {
			return currentColor;
		}

		//Implement the one method defined by TableCellEditor.
		public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
			currentColor = (Color)value;
			return button;
		}
	}

/*
class ColorEditor extends AbstractCellEditor implements TableCellEditor {

  private JButton delegate = new JButton();

  Color savedColor;

  public ColorEditor() {
    ActionListener actionListener = new ActionListener() {
      public void actionPerformed(ActionEvent actionEvent) {
        Color color = JColorChooser.showDialog(delegate, "Color Chooser", savedColor);
        ColorEditor.this.changeColor(color);
      }
    };
    delegate.addActionListener(actionListener);
  }

  public Object getCellEditorValue() {
    return savedColor;
  }

  private void changeColor(Color color) {
    if (color != null) {
      savedColor = color;
      delegate.setBackground(color);
    }
  }

  public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,
      int row, int column) {
    changeColor((Color) value);
    return delegate;
  }
}
*/

	private class MyDefaultCellRenderer extends DefaultTableCellRenderer {

		@Override
		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
			JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

			// Texte noir par défaut, gris si table inactive (si activator décoché)
			if (isActive()) {
				label.setForeground(Color.black);
				if (isSelected)
					label.setBackground(table.getSelectionBackground());
				else
					label.setBackground(Color.white);
				
			} else {
				label.setForeground(Color.gray);
				label.setBackground(Color.lightGray);
			}

			// En gras pour la 1ere colonne
			if (column==0)
				label.setFont(label.getFont().deriveFont(Font.BOLD));

			// Affichage de la cell en rouge avec tooltip text si un message d'erreur
			//  a été déclaré pour cette cellule
			String err_msg = null;
			if (errorMsg != null)
				err_msg = errorMsg.get(row)[column];
			if (err_msg!=null && err_msg.length()>0) {
				label.setBorder(BorderFactory.createLineBorder(DlgParams_ls.COLOR_INVALID));
				label.setToolTipText(err_msg);
			} else {
				label.setToolTipText(null);
			}
			return label;
		}

	}

	private class MyColorRenderer extends JLabel implements TableCellRenderer {
		Border unselectedBorder = null;
		Border selectedBorder = null;

		public MyColorRenderer () {
			setOpaque(true); //MUST do this for background to show up.
		}

		public Component getTableCellRendererComponent(
								JTable table, Object color,
								boolean isSelected, boolean hasFocus,
								int row, int column) {

			// Si une erreur est déclarée pour cette cellule -> bordure rouge
			String err_msg = null;
			if (errorMsg != null)
				err_msg = errorMsg.get(row)[column];
			if (err_msg!=null && err_msg.length()>0) {
				setBorder(BorderFactory.createLineBorder(DlgParams_ls.COLOR_INVALID));
				setToolTipText(err_msg);
			} else {
				setToolTipText(null);
			}

			if (color==null) {
				if (isSelected)
					setBackground(table.getSelectionBackground());
				else
					setBackground(table.getBackground());

			} else {

				Color newColor = (Color)color;

				if (isActive()) {
					setBackground(newColor);

					if (isSelected)
						setBorder(BorderFactory.createMatteBorder(1,2,1,2, table.getSelectionBackground()));
					else
						setBorder(BorderFactory.createMatteBorder(1,2,1,2, Color.white));

					setForeground(Utils.getBestTextColor(newColor));

				} else {
					Color colorGrisee = Utils.getColorGrisee(newColor);
					setBackground(colorGrisee);
					if (isSelected)
						setBorder(BorderFactory.createMatteBorder(1,2,1,2, table.getSelectionBackground()));
					else
						setBorder(BorderFactory.createMatteBorder(1,2,1,2, Color.lightGray));

					setForeground(Color.darkGray);
				}

				setText(" RGB value: " + newColor.getRed() + ", " + newColor.getGreen() + ", " + newColor.getBlue());
				//setToolTipText(" RGB value: " + newColor.getRed() + ", " + newColor.getGreen() + ", " + newColor.getBlue());
			}
			
			return this;
		}
	}

}
